闭包定义
对闭包的具体定义有很多种说法,这些说法大体可以分为两类:
闭包是其词法上下文中引用了自由变量的函数.
闭包是由函数和其相关的引用环境组合而成的实体.
词法
:变量的作用域是由它在源码中所处位置决定的.
很多人都觉得闭包是一个很难理解的知识点,其实不然,不管它的定义有多难理解,我们只需自己对它形成一种自己可以理解的定义就可以了,并保证这种自我理解定义的正确性和可行性.
在这里来看,闭包不管他是一个函数还是一个实体,它给我的理解就是一个函数可以访问当前上下文的环境变量.在这样看来,不管是函数内部的函数,还是单个定义的函数,有函数的地方就存在闭包.
闭包的特性和好处
闭包的特性:
函数内部可以引用外部的参数和变量.
参数和变量不会被垃圾回收机制回收.
使用闭包的好处
希望一个变量长期保存在内存中
可以拥有私有成员
在函数内部存在函数形式的闭包中,可以避免全局变量的污染
举例
函数内部函数
function init() {
var name = "closure";
function displayName() {
alert(name);
}
displayName();
}
init();
函数 init()
创建了一个局部变量 name
,然后定义了名为 displayName()
的函数。 displayName()
是一个内部函数——定义于 init()
之内且仅在该函数体内可用。displayName()
没有任何自己的局部变量,然而它可以访问到外部函数的变量,即可以使用父函数中声明的 name
变量。
用闭包模拟私有方法
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
alert(Counter1.value()); /* 提示 0 */
Counter1.increment();
Counter1.increment();
alert(Counter1.value()); /* 提示 2 */
Counter1.decrement();
alert(Counter1.value()); /* 提示 1 */
alert(Counter2.value()); /* 提示 0 */
请注意两个计数器是如何维护它们各自的独立性的。每次调用 makeCounter()
函数期间,其环境是不同的。每次调用中, privateCounter
中含有不同的实例。
这种形式的闭包提供了许多通常由面向对象编U所享有的益处,尤其是数据隐藏和封装。
在循环中创建闭包:一个常见错误
<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
}
setupHelp();
运行这段代码后,您会发现它没有达到想要的效果。无论焦点在哪个输入域上,显示的都是关于年龄的消息,
该问题的原因在于赋给 onfocus
是闭包(setupHelp)中的匿名函数而不是闭包对象;
解决这个问题的一种方案是使onfocus指向一个新的闭包对象。
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function makeHelpCallback(help) {
return function() {
showHelp(help);
};
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
}
}
setupHelp();
性能考量
如果不是因为某些特殊任务而需要闭包,在没有必要的情况下,在其它函数中创建函数是不明智的,因为闭包对脚本性能具有负面影响,包括处理速度和内存消耗。
例如,在创建新的对象或者类时,方法通常应该关联于对象的原型,而不是定义到对象的构造器中。原因是这将导致每次构造器被调用,方法都会被重新赋值一次(也就是说,为每一个对象的创建)。
考虑以下虽然不切实际但却说明问题的示例:
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
this.getName = function() {
return this.name;
};
this.getMessage = function() {
return this.message;
};
}
上面的代码并未利用到闭包的益处,因此,应该修改为如下常规形式:
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype = {
getName: function() {
return this.name;
},
getMessage: function() {
return this.message;
}
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。